#include "stm32l4xx_hal.h"
#include "flash.h"
#define FLASH_BANK_2              ((uint32_t)0x02)
/**
 * @brief	��ȡָ����ַ����(32λ����)
 *
 * @param   faddr	����ַ
 *
 * @return  uint32_t 		��ȡ������
 */
uint32_t  STMFLASH_ReadWord(uint32_t  faddr)
{
    return *(uint32_t *)faddr;
}


/**
 * @brief	ʶ��FLASH��ַ����BANK(��оƬ������BANK)
 *
 * @param   Addr	FLASH��ַ
 *
 * @return  uint32_t 		FLASH��ַ����BANK��
 */
static uint32_t Get_FLASH_Bank(uint32_t Addr)
{
    uint32_t bank = 0;

    /* No Bank swap */
    if(Addr < (FLASH_BASE + FLASH_BANK_SIZE))
    {
        bank = FLASH_BANK_1;
    }

    else
    {
        bank = FLASH_BANK_2;
    }

    return bank;
}


/**
 * @brief	������д������(64λ)
 *
 * @param  	WriteAddr	��ʼ��ַ(�˵�ַ����Ϊ8�ı���!!)
 * @param  	pBuffer		����ָ��
 * @param  	NumToWrite	����(64λ)��(����Ҫд���64λ���ݵĸ���)
 *
 * @return  void
 */
void STMFLASH_Write_NoCheck(uint32_t  WriteAddr, uint64_t *pBuffer, uint32_t  NumToWrite)
{
    uint32_t  i;

    for(i = 0; i < NumToWrite; i++)
    {
        if( HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, WriteAddr, pBuffer[i]) != HAL_OK )
		{
			return;		//д����󣡣���
		}
        WriteAddr += 8; //��ַ����
    }
}


/**
 * @brief	��ָ����ַ��ʼд��ָ�����ȵ����ݣ�ÿ��д������������(64λ)
 *
 * @param  	WriteAddr	��ʼ��ַ(�˵�ַ����Ϊ8�ı���!!)
 * @param  	pBuffer		����ָ��
 * @param  	NumToWrite	����(64λ)��(����Ҫд���64λ���ݵĸ���)
 *
 * @return  void
 */
#define STM_SECTOR_SIZE	2048

uint8_t STMFLASH_Write(uint32_t  WriteAddr, uint64_t *pBuffer, uint32_t  NumToWrite)
{
	uint8_t flag=0;
    FLASH_EraseInitTypeDef FlashEraseInit;
    uint32_t	SectorError;
    uint64_t STMFLASH_BUF[STM_SECTOR_SIZE / 8]; //�����2K�ֽ�
    uint8_t flash_read=0;
    flash_read=0;
    NumToWrite=NumToWrite/8;

    if(WriteAddr < STM32_FLASH_BASE || WriteAddr % 8)return 0;	//�Ƿ���ַ

    uint16_t  i;
    uint32_t  secpos;	   //������ַ
    uint16_t  secoff;	   //������ƫ�Ƶ�ַ(16λ�ּ���)
    uint16_t  secremain; //������ʣ���ַ(16λ�ּ���)
    uint32_t  offaddr;   //ȥ��0X08000000��ĵ�ַ

    offaddr = WriteAddr - STM32_FLASH_BASE;		//ʵ��ƫ�Ƶ�ַ.
    secpos = offaddr / STM_SECTOR_SIZE;			//������ַ
    secoff = (offaddr % STM_SECTOR_SIZE) / 8;	//��ҳ�ڵ�ƫ��(8���ֽ�Ϊ������λ)
    secremain = STM_SECTOR_SIZE / 8 - secoff;	//����ʣ��ռ��С

    if(NumToWrite <= secremain)secremain = NumToWrite; //�����ڸ�������Χ

    HAL_FLASH_Unlock();             //����

    while(1)
    {
        STMFLASH_Read(secpos * STM_SECTOR_SIZE, (uint32_t *)STMFLASH_BUF, STM_SECTOR_SIZE / 4);	//������ҳȫ������

        for(i = 0; i < secremain; i++)
        {
            if(STMFLASH_BUF[secoff + i] != 0xFFFFFFFFFFFFFFFF) break;//�з�0xFFFFFFFFFFFFFFFF�ĵط�,Ҫ�����������
        }

        if(i < secremain)//��Ҫ����
        {
            FlashEraseInit.TypeErase = FLASH_TYPEERASE_PAGES; //�������ͣ���������
            FlashEraseInit.Banks = Get_FLASH_Bank(secpos * STM_SECTOR_SIZE + STM32_FLASH_BASE); //Ҫ������Bank
            FlashEraseInit.Page = secpos;    			//Ҫ������ҳ
            FlashEraseInit.NbPages = 1;      			//��Ҫɾ����ҳ��

            if(HAL_FLASHEx_Erase(&FlashEraseInit, &SectorError) != HAL_OK)
            {
            	flag=1;
                break;//����������
            }

            for(i = 0; i < secremain; i++) //����
            {
                STMFLASH_BUF[i + secoff] = pBuffer[i];
            }

            STMFLASH_Write_NoCheck(secpos * STM_SECTOR_SIZE + STM32_FLASH_BASE, STMFLASH_BUF, STM_SECTOR_SIZE / 8);
        }

        else//д�Ѿ������˵�,ֱ��д������ʣ������.
        {
            STMFLASH_Write_NoCheck(WriteAddr, pBuffer, secremain);
        }

        if(NumToWrite == secremain)
        {
        	break; //д�������
        }

        else
        {
            secpos++;				//������ַ��1
            secoff = 0;				//ƫ��λ��Ϊ0
            pBuffer += secremain;  	//ָ��ƫ��
            WriteAddr += (secremain * 8); //д��ַƫ��
            NumToWrite -= secremain;	//�ֽ�(64λ)���ݼ�

            if(NumToWrite > (STM_SECTOR_SIZE / 8))secremain = STM_SECTOR_SIZE / 8; //��һ����������д����

            else secremain = NumToWrite; //��һ����������д����
        }
    }

    HAL_FLASH_Lock();           //����
    return flag;
}

/**
 * @brief	��ָ����ַ��ʼ����ָ�����ȵ����ݣ�ÿ�ζ���һ����(32λ)
 *
 * @param  	ReadAddr	��ʼ��ַ
 * @param  	pBuffer		����ָ��
 * @param  	NumToRead	������(32λ)��
 *
 * @return  void
 */
void STMFLASH_Read(uint32_t  ReadAddr, uint32_t  *pBuffer, uint32_t  NumToRead)
{
    uint32_t  i;
//    if(flash_read==0)
//    {
//    	flash_read=1;
//    }
//    else
//    {
//    	return;
//    }
    NumToRead=NumToRead/4;

    for(i = 0; i < NumToRead; i++)
    {
        pBuffer[i] = STMFLASH_ReadWord(ReadAddr); //��ȡ4���ֽ�.
        ReadAddr += 4; //ƫ��4���ֽ�.
    }
//    HAL_FLASH_Lock();           //����
}




